import * as THREE from 'three';

// 边界框接口
export interface BoundingBox {
  min: THREE.Vector3;
  max: THREE.Vector3;
}

// 碰撞结果接口
export interface CollisionResult {
  hasCollision: boolean;
  normal?: THREE.Vector3;
  penetrationDepth?: number;
  contactPoint?: THREE.Vector3;
  collisionObject?: THREE.Object3D;
}

// 碰撞检测器类
export class CollisionDetector {
  private carBounds: THREE.Box3;
  private buildingBounds: Map<THREE.Object3D, THREE.Box3>;
  private spatialGrid: Map<string, THREE.Object3D[]>;
  private gridSize: number;

  constructor(gridSize: number = 10) {
    this.carBounds = new THREE.Box3();
    this.buildingBounds = new Map();
    this.spatialGrid = new Map();
    this.gridSize = gridSize;
  }

  /**
   * 注册建筑物到碰撞检测系统
   * @param building 建筑物对象
   */
  public addBuilding(building: THREE.Object3D): void {
    const bounds = this.calculateAABB(building);
    console.log('注册建筑物边界框:', bounds);
    this.buildingBounds.set(building, bounds);
    this.addToSpatialGrid(building, bounds);
    console.log(`建筑物注册成功，当前总数: ${this.buildingBounds.size}`);
  }

  /**
   * 移除建筑物从碰撞检测系统
   * @param building 建筑物对象
   */
  public removeBuilding(building: THREE.Object3D): void {
    const bounds = this.buildingBounds.get(building);
    if (bounds) {
      this.removeFromSpatialGrid(building, bounds);
      this.buildingBounds.delete(building);
    }
  }

  /**
   * 检测车辆与所有建筑物的碰撞
   * @param carGroup 车辆组对象
   * @returns 碰撞结果数组
   */
  public checkCollisions(carGroup: THREE.Group): CollisionResult[] {
    // 更新车辆边界框
    this.updateCarBounds(carGroup);
    
    const collisions: CollisionResult[] = [];
    const nearbyBuildings = this.getNearbyBuildings(carGroup.position);

    // 调试输出
    if (nearbyBuildings.size === 0) {
      console.log('没有找到附近的建筑物，车辆位置:', carGroup.position);
      console.log('总建筑物数量:', this.buildingBounds.size);
    }

    // 检测与附近建筑物的碰撞
    for (const building of nearbyBuildings) {
      const buildingBounds = this.buildingBounds.get(building);
      if (buildingBounds) {
        const collision = this.testAABBIntersection(this.carBounds, buildingBounds, building);
        if (collision.hasCollision) {
          collisions.push(collision);
        }
      }
    }

    return collisions;
  }

  /**
   * 计算对象的AABB边界框
   * @param object 3D对象
   * @returns 边界框
   */
  private calculateAABB(object: THREE.Object3D): THREE.Box3 {
    const box = new THREE.Box3();
    
    try {
      // 使用 setFromObject 直接计算整个对象的边界框
      box.setFromObject(object);
      
      // 如果边界框为空或太小，使用默认大小
      if (box.isEmpty() || box.getSize(new THREE.Vector3()).length() < 0.1) {
        console.warn('对象边界框计算异常，使用默认边界框:', object);
        const position = object.position;
        const scale = object.scale;
        
        // 根据对象类型设置默认大小
        let defaultSize = new THREE.Vector3(3, 3, 3); // 建筑物默认大小
        
        // 如果是树木（根据子对象判断）
        let hasTreeComponents = false;
        object.traverse((child) => {
          if (child instanceof THREE.Mesh) {
            const geometry = child.geometry;
            if (geometry instanceof THREE.CylinderGeometry || geometry instanceof THREE.SphereGeometry) {
              hasTreeComponents = true;
            }
          }
        });
        
        if (hasTreeComponents) {
          defaultSize = new THREE.Vector3(6, 8, 6); // 树木更大的范围
        }
        
        // 应用缩放
        defaultSize.multiply(scale);
        
        box.setFromCenterAndSize(position, defaultSize);
        console.log('使用默认边界框:', {
          position: position,
          size: defaultSize,
          box: { min: box.min, max: box.max }
        });
      } else {
        console.log('成功计算边界框:', {
          position: object.position,
          size: box.getSize(new THREE.Vector3()),
          box: { min: box.min, max: box.max }
        });
      }
      
    } catch (error) {
      console.error('计算边界框时出错:', error);
      // 备用方案：使用位置创建默认边界框
      const position = object.position;
      const size = new THREE.Vector3(3, 3, 3);
      box.setFromCenterAndSize(position, size);
    }

    return box;
  }

  /**
   * 更新车辆边界框
   * @param carGroup 车辆组对象
   */
  private updateCarBounds(carGroup: THREE.Group): void {
    this.carBounds.setFromObject(carGroup);
    
    // 稍微扩大车辆边界框以提供更好的碰撞检测
    const expansion = new THREE.Vector3(0.1, 0.1, 0.1);
    this.carBounds.expandByVector(expansion);
  }

  /**
   * 测试两个AABB的相交
   * @param box1 第一个边界框
   * @param box2 第二个边界框
   * @param collisionObject 碰撞对象
   * @returns 碰撞结果
   */
  private testAABBIntersection(
    box1: THREE.Box3, 
    box2: THREE.Box3, 
    collisionObject?: THREE.Object3D
  ): CollisionResult {
    if (!box1.intersectsBox(box2)) {
      return { hasCollision: false };
    }

    // 计算重叠区域
    const intersection = box1.clone().intersect(box2);
    
    // 计算穿透深度和碰撞法向量
    const size1 = box1.getSize(new THREE.Vector3());
    const size2 = box2.getSize(new THREE.Vector3());
    const center1 = box1.getCenter(new THREE.Vector3());
    const center2 = box2.getCenter(new THREE.Vector3());
    
    const distance = center1.clone().sub(center2);
    const penetration = intersection.getSize(new THREE.Vector3());
    
    // 找到最小穿透轴
    let minAxis = 0;
    let minPenetration = penetration.x;
    
    if (penetration.y < minPenetration) {
      minAxis = 1;
      minPenetration = penetration.y;
    }
    
    if (penetration.z < minPenetration) {
      minAxis = 2;
      minPenetration = penetration.z;
    }

    // 计算法向量
    const normal = new THREE.Vector3();
    normal.setComponent(minAxis, distance.getComponent(minAxis) > 0 ? 1 : -1);

    // 计算接触点
    const contactPoint = intersection.getCenter(new THREE.Vector3());

    return {
      hasCollision: true,
      normal,
      penetrationDepth: minPenetration,
      contactPoint,
      collisionObject
    };
  }

  /**
   * 将建筑物添加到空间网格
   * @param building 建筑物对象
   * @param bounds 边界框
   */
  private addToSpatialGrid(building: THREE.Object3D, bounds: THREE.Box3): void {
    const gridKeys = this.getGridKeys(bounds);
    
    for (const key of gridKeys) {
      if (!this.spatialGrid.has(key)) {
        this.spatialGrid.set(key, []);
      }
      this.spatialGrid.get(key)!.push(building);
    }
  }

  /**
   * 从空间网格移除建筑物
   * @param building 建筑物对象
   * @param bounds 边界框
   */
  private removeFromSpatialGrid(building: THREE.Object3D, bounds: THREE.Box3): void {
    const gridKeys = this.getGridKeys(bounds);
    
    for (const key of gridKeys) {
      const buildings = this.spatialGrid.get(key);
      if (buildings) {
        const index = buildings.indexOf(building);
        if (index !== -1) {
          buildings.splice(index, 1);
        }
        if (buildings.length === 0) {
          this.spatialGrid.delete(key);
        }
      }
    }
  }

  /**
   * 获取边界框对应的网格键
   * @param bounds 边界框
   * @returns 网格键数组
   */
  private getGridKeys(bounds: THREE.Box3): string[] {
    const keys: string[] = [];
    const min = bounds.min;
    const max = bounds.max;
    
    const minGridX = Math.floor(min.x / this.gridSize);
    const minGridZ = Math.floor(min.z / this.gridSize);
    const maxGridX = Math.floor(max.x / this.gridSize);
    const maxGridZ = Math.floor(max.z / this.gridSize);
    
    for (let x = minGridX; x <= maxGridX; x++) {
      for (let z = minGridZ; z <= maxGridZ; z++) {
        keys.push(`${x},${z}`);
      }
    }
    
    return keys;
  }

  /**
   * 获取车辆附近的建筑物
   * @param carPosition 车辆位置
   * @returns 附近的建筑物数组
   */
  private getNearbyBuildings(carPosition: THREE.Vector3): Set<THREE.Object3D> {
    const nearbyBuildings = new Set<THREE.Object3D>();
    
    // 计算车辆所在的网格及周围网格
    const gridX = Math.floor(carPosition.x / this.gridSize);
    const gridZ = Math.floor(carPosition.z / this.gridSize);
    
    // 检查 3x3 网格区域
    for (let x = gridX - 1; x <= gridX + 1; x++) {
      for (let z = gridZ - 1; z <= gridZ + 1; z++) {
        const key = `${x},${z}`;
        const buildings = this.spatialGrid.get(key);
        if (buildings) {
          buildings.forEach(building => nearbyBuildings.add(building));
        }
      }
    }
    
    return nearbyBuildings;
  }

  /**
   * 获取所有建筑物边界框（用于调试）
   * @returns 建筑物边界框映射
   */
  public getBuildingBounds(): Map<THREE.Object3D, THREE.Box3> {
    return this.buildingBounds;
  }

  /**
   * 获取车辆当前边界框（用于调试）
   * @returns 车辆边界框
   */
  public getCarBounds(): THREE.Box3 {
    return this.carBounds.clone();
  }

  /**
   * 清理资源
   */
  public dispose(): void {
    this.buildingBounds.clear();
    this.spatialGrid.clear();
  }
}